{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Composing Objects"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import holoviews as hv\n",
    "hv.extension('bokeh')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Instantly viewable HoloViews objects include elements (discussed already) and containers (collections of elements or other containers).  Here we'll introduce two types of containers for collecting viewable objects, each typically created from the existing objects using a convenient operator syntax:\n",
    "\n",
    "   1. ** [``Layout``](../reference/containers/bokeh/Layout.ipynb) (``+``):** A collection of any HoloViews objects to be displayed side by side. \n",
    "   2. **[``Overlay``](../reference/containers/bokeh/Overlay.ipynb) (``*``):** A collection of HoloViews objects to be displayed overlaid on one another with the same axes.\n",
    "\n",
    "The Layout and Overlay containers allow you to mix types in any combination, and have an ordering but no numerical or categorical key dimension with which to index the objects.  In contrast, the [Dimensioned containers](./05-Dimensioned_Containers.ipynb) discussed later, such as [``HoloMap``](../reference/containers/bokeh/HoloMap.ipynb) , [``GridSpace``](../reference/containers/bokeh/GridSpace.ipynb), [``NdOverlay``](../reference/containers/bokeh/NdOverlay.ipynb), and [``NdLayout``](../reference/containers/bokeh/NdLayout.ipynb), do not allow mixed types, and each item has an associated numerical or categorical index (key).\n",
    "\n",
    "Because you can compose a mix of any HoloViews elements into layouts and overlays, these types of container are very common, which is why they have dedicated composition operators. This user guide describes how you can build and organize your data using these two types of composition.\n",
    "\n",
    "To show how layouts and overlays work with heterogeneous types, we will use these two elements throughout this notebook:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "xs = [0.1* i for i in range(100)]\n",
    "curve =  hv.Curve((xs, [np.sin(x) for x in xs]))\n",
    "scatter =  hv.Scatter((xs[::5], np.linspace(0,1,20)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. ``Layout``"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A ``Layout`` can contain any HoloViews object except an ``NdLayout``. (See [Building Composite Objects](06-Building_Composite_Objects.ipynb) for the full details about the ways containers can be composed.)\n",
    "\n",
    "You can build a ``Layout`` from two or more HoloViews objects of any type by using the ``+`` operator:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "curve + scatter"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In this example, we have a ``Layout`` composed of a ``Curve`` element and a ``Scatter`` element, and they happen to share the same ``x`` and ``y`` dimensions. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Building a ``Layout`` from a list\n",
    "\n",
    "If the ``+`` syntax is not convenient, you can also build a ``Layout`` using its constructor directly, which is useful if you want to create a ``Layout`` of an arbitrary length:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "curve_list   = [hv.Curve((xs, [np.sin(f*x) for x in xs])) for f in [0.5, 0.75]]\n",
    "scatter_list = [hv.Scatter((xs[::5], f*np.linspace(0,1,20))) for f in [-0.5, 0.5]]\n",
    "\n",
    "layout = hv.Layout(curve_list + scatter_list).cols(2)\n",
    "layout"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note the use of the ``.cols`` method to specify the number of columns, wrapping to the next row in scanline order (left to right, then top to bottom)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### A ``Layout`` has two-level attribute access\n",
    "\n",
    "``Layout`` and ``Overlay`` are tree-based data structures that can hold arbitrarily heterogenous collections of HoloViews objects, and are quite different from the dictionary-like dimensioned containers (which will be described in later guides).\n",
    "\n",
    "As mentioned previously in [Annotating Data](01-Annotating_Data.ipynb), HoloViews objects have string ``group`` and ``label`` parameters, which can be used to select objects in the ``Layout`` using two-level attribute access. First let us see how to index the above example, where the ``group`` and ``label`` parameters were left unspecified on creation:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(layout)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As you can see, the ``layout`` object consists of four different elements, each mapping from ``x`` to ``y``.  You can use the \"dot\" syntax shown in the repr to select individual elements from the layout:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "layout2 = layout.Curve.I + layout.Scatter.II\n",
    "layout2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here we create a second layout by indexing two elements from our earlier ``layout`` object and using ``+`` between them. We see that the first level of indexing is the ``group`` string (which defaults to the element class name) followed by the label, which wasn't set and is therefore mapped to an automatically generated Roman numeral (I,II,III,IV, etc.). \n",
    "\n",
    "As group and label were again not specified, our new ``Layout`` will also use ``Curve.I`` for the curve, but as there is only one scatter element, it will have ``Scatter.I`` to index the scatter:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "layout2.Scatter.I"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Using ``group`` and ``label`` with ``Layout``"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now let's return to the first simple layout example, this time setting a group and label as introduced in the [Annotating Data](./01-Annotating_Data.ipynb) guide:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "xs = [0.1* i for i in range(100)]\n",
    "\n",
    "low_freq  = hv.Curve((xs, [np.sin(x) for x in xs]),    group='Sinusoid',      label='Low Frequency')\n",
    "linpoints = hv.Scatter((xs[::5], np.linspace(0,1,20)), group='Linear Points', label='Demo')\n",
    "\n",
    "labelled = low_freq + linpoints\n",
    "labelled"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As you can see, the group and label are used for titling the plots.  They also determine how the objects are accessed:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "labelled.Linear_Points.Demo + labelled.Sinusoid.Low_Frequency"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You are encouraged to use the group and label names as appropriate for organizing your own data.  They should let you easily refer to groups of data that are meaningful to your domain, e.g. for [Applying Customization](./03-Applying_Customization.ipynb)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. ``Overlay``\n",
    "\n",
    "An ``Overlay`` can contain any HoloViews elements, but the only container type it can contain is ``NdOverlay``. [Building Composite Objects](06-Building_Composite_Objects.ipynb) provides the full details on how containers can be composed.\n",
    "\n",
    "Other than being composed with ``*`` and displaying elements together in the same space, ``Overlay`` shares many of the same concepts as layout. The rest of this section will show the overlay equivalents of the manipulations shown above for layout. \n",
    "\n",
    "First, composition with ``*`` instead of ``+`` results in a single overlaid plot, rather than side-by-side plots:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "curve * scatter"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Building ``Overlay`` from a list"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "An ``Overlay`` can be built explicitly from a list, just like a ``Layout``:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "curve_list   = [hv.Curve((xs, [np.sin(f*x) for x in xs])) for f in [0.5, 0.75]]\n",
    "scatter_list = [hv.Scatter((xs[::5], f*np.linspace(0,1,20))) for f in [-0.5, 0.5]]\n",
    "overlay = hv.Overlay(curve_list + scatter_list)\n",
    "overlay"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As you can see, a special feature of ``Overlay`` compared to ``Layout`` is that overlays use *color cycles* to help keep the overlaid plots distinguishable, which you can learn about in [Applying Customization](./03-Applying_Customization.ipynb)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### ``Overlay`` also has two-level attribute access\n",
    "\n",
    "Like ``Layout``, ``Overlay`` is fundamentally a tree structure holding arbitrarily heterogenous HoloViews objects, unlike the dimensioned containers. ``Overlay`` objects also make use of the ``group`` and ``label`` parameters, introduced in [Annotating Data](01-Annotating_Data.ipynb), for two-level attribute access.\n",
    "\n",
    "Once again, let us see how to index the above example where the ``group`` and ``label`` parameters were left unspecified:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(overlay)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "overlay.Curve.I * overlay.Scatter.II"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here we create a second overlay by indexing two elements from our earlier ``overlay`` object and using ``*`` between them. We see that the first level is the ``group`` string (which defaults to the element class name) followed by the label, which wasn't set and is therefore mapped to a Roman numeral."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Using ``group`` and ``label`` with ``Overlay``\n",
    "\n",
    "Now let's return to the first simple overlay example, this time setting ``group`` and ``label`` as introduced in the [Annotating Data](./01-Annotating_Data.ipynb) guide:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "high_freq =  hv.Curve((xs, [np.sin(2*x) for x in xs]), group='Sinusoid', label='High Frequency')\n",
    "labelled = low_freq * high_freq * linpoints\n",
    "labelled"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Once again, this example follows the corresponding ``Layout`` example, although this time we added a high-frequency curve to demonstrate how ``group`` and ``label`` are now used to generate the legend (as opposed to the title, as it was for ``Layout``).\n",
    "\n",
    "The following example shows how ``group`` and ``label`` affect access:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "labelled.Linear_Points.Demo * labelled.Sinusoid.High_Frequency * labelled.Sinusoid.Low_Frequency"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This new re-ordered ``Overlay`` switches the z-ordering as well as the legend color of the two sinusoidal curves. The colors and other plot options can be set for specific groups and labels as described in \n",
    "[Applying Customization](./03-Applying_Customization.ipynb).\n",
    "\n",
    "\n",
    "## Layouts of overlays\n",
    "\n",
    "Of course, layouts work with both elements and overlays:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "overlay + labelled + labelled.Sinusoid.Low_Frequency"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Tab completion\n",
    "\n",
    "Both ``Layout`` and ``Overlay`` are designed to be easy to explore and inspect with tab completion. Try running:\n",
    "\n",
    "```python\n",
    "overlay.[tab]\n",
    "```\n",
    "\n",
    "or\n",
    "\n",
    "```python\n",
    "layout.[tab]\n",
    "```\n",
    "\n",
    "\n",
    "In a code cell and you should see the first levels of indexing (``Curve`` and ``Scatter``) conveniently listed at the top. If this is not the case, you may need to enable improved tab-completion as described in [Configuring HoloViews](./Installing_and_Configuring.ipynb).\n",
    "\n",
    "Having seen how to compose viewable objects, the next section shows how to [apply customizations](./03-Applying_Customization.ipynb)."
   ]
  }
 ],
 "metadata": {
  "language_info": {
   "name": "python",
   "pygments_lexer": "ipython3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
